// Learn TypeScript:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/life-cycle-callbacks.html

import { Http } from "./Http";

const { ccclass, property } = cc._decorator;

///////// 消息回调接口
export interface socket_message {
    (d: any): void;
}

export default class Socket {
    static on_message_handle: Map<string, socket_message> = new Map(); /////// 所有相应消息的组件

    static url = "ws://120.26.171.101:8888";
    // static url = "ws://192.168.110.249:19000";
    // static url = null;

    static _ws: WebSocket = null;

    static _err_event: Function = null;

    public static init(on_open: Function) {
        this._ws = new WebSocket(this.url);
        this._ws.onopen = () => {
            console.log("socket 连接成功...");
            on_open();
        };

        this._ws.onmessage = (evt) => {
            if (cc.sys.isNative) {
                let a = new Uint8Array(evt.data);
                let data = JSON.parse(this.Utf8ArrayToStr(a));

                if (data.m == "ping") { ////// 心跳
                    Socket._ws.send(JSON.stringify({ c: 'game', m: 'pong', data: {} }));
                } else {
                    Socket.on_message(data);
                }
            } else {
                const READER = new FileReader();
                READER.addEventListener("loadend", (e: any) => {
                    // 此处输出结果
                    var data = JSON.parse(e.target.result)
                    if (data.m == "ping") { ////// 心跳
                        Socket._ws.send(JSON.stringify({ c: 'game', m: 'pong', data: {} }));
                    } else {
                        Socket.on_message(data);
                    }
                });
                READER.readAsText(evt.data);
            }
        };

        this._ws.onclose = () => {
            // 关闭 websocket
            console.log("连接已关闭...");
            Socket._err_event && Socket._err_event();
        };

        this._ws.onerror = () => {
            console.log("连接错误 ...");
            this._ws.onclose = null;
            this.close();
            Socket._err_event && Socket._err_event();
        }
    }

    public static send(c: string, _m: string, _data: any) {
        let d = { c, m: _m, data: _data };

        try {
            this._ws.send(JSON.stringify(d));
        } catch (error) {
            console.error("send() 出错 : " + JSON.stringify(d));
            console.error(error);
        }
    }

    private static on_message(d: any) {
        this.on_message_handle.forEach((value, key) => {

            /////////////////////模拟数据//////////////////////////

            //////////////////////////////////////////////////////
            value(d);
        });
    }

    public static close() {
        this.set_error_listen(null);
        try {
            this._ws && this._ws.close();
        } catch (error) {
            console.error(error);
        }
        this._ws = null;
    }

    public static get_state(): number {
        return this._ws.readyState;
    }

    public static set_error_listen(e: Function) {
        this._err_event = e;
    }


    /******************************************
     *     处理器
     ******************************************/
    public static add_handle(key: string, h: socket_message) {
        if (this.on_message_handle.has(key)) {
            console.log("add_handle()  key已存在 " + key);
        } else {
            this.on_message_handle.set(key, h);
        }
    }

    public static del_handle(key: string) {
        if (this.on_message_handle.has(key)) {
            this.on_message_handle.delete(key);
        } else {
            console.error("del_handle()  key 没有找到 " + key);
        }
    }

    public static clean_handle() {
        this.on_message_handle.clear();
    }


    /************************************************
     *          转码
     ************************************************/
    private static Utf8ArrayToStr(array) {
        var out, i, len, c;
        var char2, char3;

        out = "";
        len = array.length;
        i = 0;
        while (i < len) {
            c = array[i++];
            switch (c >> 4) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    // 0xxxxxxx
                    out += String.fromCharCode(c);
                    break;
                case 12:
                case 13:
                    // 110x xxxx   10xx xxxx
                    char2 = array[i++];
                    out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
                    break;
                case 14:
                    // 1110 xxxx  10xx xxxx  10xx xxxx
                    char2 = array[i++];
                    char3 = array[i++];
                    out += String.fromCharCode(
                        ((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
                    );
                    break;
            }
        }
        return out;
    }


    /******************************************************
     *              主动发起 ping-pong
     ******************************************************/
    static id_ping_pong = null;
    public static init_ping_pong(time: number) {
        this.id_ping_pong = setInterval(() => {
            Socket.send("game", "ping", {
            });
        }, time);
    }

    public static stop_ping_pong() {
        clearInterval(this.id_ping_pong);
    }
}   
